/**
 * shape基类
 * @module zrender/shape/Base
 * @author  Kener (@Kener-林峰, kener.linfeng@gmail.com)
 *          errorrik (errorrik@gmail.com)
 */
/**
 * @typedef {Object} IBaseShapeStyle
 * @property {string} [brushType='fill']
 * @property {string} [color='#000000'] 填充颜色
 * @property {string} [strokeColor='#000000'] 描边颜色
 * @property {string} [lineCape='butt'] 线帽样式，可以是 butt, round, square
 * @property {number} [lineWidth=1] 描边宽度
 * @property {number} [opacity=1] 绘制透明度
 * @property {number} [shadowBlur=0] 阴影模糊度，大于0有效
 * @property {string} [shadowColor='#000000'] 阴影颜色
 * @property {number} [shadowOffsetX=0] 阴影横向偏移
 * @property {number} [shadowOffsetY=0] 阴影纵向偏移
 * @property {string} [text] 图形中的附加文本
 * @property {string} [textColor='#000000'] 文本颜色
 * @property {string} [textFont] 附加文本样式，eg:'bold 18px verdana'
 * @property {string} [textPosition='end'] 附加文本位置, 可以是 inside, left, right, top, bottom
 * @property {string} [textAlign] 默认根据textPosition自动设置，附加文本水平对齐。
 *                                可以是start, end, left, right, center
 * @property {string} [textBaseline] 默认根据textPosition自动设置，附加文本垂直对齐。
 *                                可以是top, bottom, middle, alphabetic, hanging, ideographic
 */
/**
 * @typedef {Object} module:zrender/shape/Base~IBoundingRect
 * @property {number} x 左上角顶点x轴坐标 
 * @property {number} y 左上角顶点y轴坐标
 * @property {number} width 包围盒矩形宽度
 * @property {number} height 包围盒矩形高度
 */


var vmlCanvasManager = window['G_vmlCanvasManager'];
var matrix = require('../tool/matrix');
var guid = require('../tool/guid');
var util = require('../tool/util');
var log = require('../tool/log');
var Transformable = require('../mixin/Transformable');
var Eventful = require('../mixin/Eventful');
function _fillText(ctx, text, x, y, textFont, textAlign, textBaseline) {
    if (textFont) {
        ctx.font = textFont;
    }
    ctx.textAlign = textAlign;
    ctx.textBaseline = textBaseline;
    var rect = _getTextRect(text, x, y, textFont, textAlign, textBaseline);
    text = (text + '').split('\n');
    var lineHeight = require('../tool/area').getTextHeight('国', textFont);
    switch (textBaseline) {
    case 'top':
        y = rect.y;
        break;
    case 'bottom':
        y = rect.y + lineHeight;
        break;
    default:
        y = rect.y + lineHeight / 2;
    }
    for (var i = 0, l = text.length; i < l; i++) {
        ctx.fillText(text[i], x, y);
        y += lineHeight;
    }
}
/**
         * 返回矩形区域，用于局部刷新和文字定位
         * @inner
         * @param {string} text
         * @param {number} x
         * @param {number} y
         * @param {string} textFont
         * @param {string} textAlign
         * @param {string} textBaseline
         */
function _getTextRect(text, x, y, textFont, textAlign, textBaseline) {
    var area = require('../tool/area');
    var width = area.getTextWidth(text, textFont);
    var lineHeight = area.getTextHeight('国', textFont);
    text = (text + '').split('\n');
    switch (textAlign) {
    case 'end':
    case 'right':
        x -= width;
        break;
    case 'center':
        x -= width / 2;
        break;
    }
    switch (textBaseline) {
    case 'top':
        break;
    case 'bottom':
        y -= lineHeight * text.length;
        break;
    default:
        y -= lineHeight * text.length / 2;
    }
    return {
        x: x,
        y: y,
        width: width,
        height: lineHeight * text.length
    };
}
/**
         * @alias module:zrender/shape/Base
         * @constructor
         * @extends module:zrender/mixin/Transformable
         * @extends module:zrender/mixin/Eventful
         * @param {Object} options 关于shape的配置项，可以是shape的自有属性，也可以是自定义的属性。
         */
var Base = function (options) {
    options = options || {};
    /**
             * Shape id, 全局唯一
             * @type {string}
             */
    this.id = options.id || guid();
    for (var key in options) {
        this[key] = options[key];
    }
    /**
             * 基础绘制样式
             * @type {module:zrender/shape/Base~IBaseShapeStyle}
             */
    this.style = this.style || {};
    /**
             * 高亮样式
             * @type {module:zrender/shape/Base~IBaseShapeStyle}
             */
    this.highlightStyle = this.highlightStyle || null;
    /**
             * 父节点
             * @readonly
             * @type {module:zrender/Group}
             * @default null
             */
    this.parent = null;
    this.__dirty = true;
    this.__clipShapes = [];
    Transformable.call(this);
    Eventful.call(this);
};
/**
         * 图形是否可见，为true时不绘制图形，但是仍能触发鼠标事件
         * @name module:zrender/shape/Base#invisible
         * @type {boolean}
         * @default false
         */
Base.prototype.invisible = false;
/**
         * 图形是否忽略，为true时忽略图形的绘制以及事件触发
         * @name module:zrender/shape/Base#ignore
         * @type {boolean}
         * @default false
         */
Base.prototype.ignore = false;
/**
         * z层level，决定绘画在哪层canvas中
         * @name module:zrender/shape/Base#zlevel
         * @type {number}
         * @default 0
         */
Base.prototype.zlevel = 0;
/**
         * 是否可拖拽
         * @name module:zrender/shape/Base#draggable
         * @type {boolean}
         * @default false
         */
Base.prototype.draggable = false;
/**
         * 是否可点击
         * @name module:zrender/shape/Base#clickable
         * @type {boolean}
         * @default false
         */
Base.prototype.clickable = false;
/**
         * 是否可以hover
         * @name module:zrender/shape/Base#hoverable
         * @type {boolean}
         * @default true
         */
Base.prototype.hoverable = true;
/**
         * z值，跟zlevel一样影响shape绘制的前后顺序，z值大的shape会覆盖在z值小的上面，
         * 但是并不会创建新的canvas，所以优先级低于zlevel，而且频繁改动的开销比zlevel小很多。
         * 
         * @name module:zrender/shape/Base#z
         * @type {number}
         * @default 0
         */
Base.prototype.z = 0;
/**
         * 绘制图形
         * 
         * @param {CanvasRenderingContext2D} ctx
         * @param {boolean} [isHighlight=false] 是否使用高亮属性
         * @param {Function} [updateCallback]
         *        需要异步加载资源的shape可以通过这个callback(e), 
         *        让painter更新视图，base.brush没用，需要的话重载brush
         */
Base.prototype.brush = function (ctx, isHighlight) {
    var style = this.beforeBrush(ctx, isHighlight);
    ctx.beginPath();
    this.buildPath(ctx, style);
    switch (style.brushType) {
    /* jshint ignore:start */
    case 'both':
        ctx.fill();
    case 'stroke':
        style.lineWidth > 0 && ctx.stroke();
        break;
    /* jshint ignore:end */
    default:
        ctx.fill();
    }
    this.drawText(ctx, style, this.style);
    this.afterBrush(ctx);
};
/**
         * 具体绘制操作前的一些公共操作
         * @param {CanvasRenderingContext2D} ctx
         * @param {boolean} [isHighlight=false] 是否使用高亮属性
         * @return {Object} 处理后的样式
         */
Base.prototype.beforeBrush = function (ctx, isHighlight) {
    var style = this.style;
    if (this.brushTypeOnly) {
        style.brushType = this.brushTypeOnly;
    }
    if (isHighlight) {
        // 根据style扩展默认高亮样式
        style = this.getHighlightStyle(style, this.highlightStyle || {}, this.brushTypeOnly);
    }
    if (this.brushTypeOnly == 'stroke') {
        style.strokeColor = style.strokeColor || style.color;
    }
    ctx.save();
    this.doClip(ctx);
    this.setContext(ctx, style);
    // 设置transform
    this.setTransform(ctx);
    return style;
};
/**
         * 绘制后的处理
         * @param {CanvasRenderingContext2D} ctx
         */
Base.prototype.afterBrush = function (ctx) {
    ctx.restore();
};
var STYLE_CTX_MAP = [
    [
        'color',
        'fillStyle'
    ],
    [
        'strokeColor',
        'strokeStyle'
    ],
    [
        'opacity',
        'globalAlpha'
    ],
    [
        'lineCap',
        'lineCap'
    ],
    [
        'lineJoin',
        'lineJoin'
    ],
    [
        'miterLimit',
        'miterLimit'
    ],
    [
        'lineWidth',
        'lineWidth'
    ],
    [
        'shadowBlur',
        'shadowBlur'
    ],
    [
        'shadowColor',
        'shadowColor'
    ],
    [
        'shadowOffsetX',
        'shadowOffsetX'
    ],
    [
        'shadowOffsetY',
        'shadowOffsetY'
    ]
];
/**
         * 设置 fillStyle, strokeStyle, shadow 等通用绘制样式
         * @param {CanvasRenderingContext2D} ctx
         * @param {module:zrender/shape/Base~IBaseShapeStyle} style
         */
Base.prototype.setContext = function (ctx, style) {
    for (var i = 0, len = STYLE_CTX_MAP.length; i < len; i++) {
        var styleProp = STYLE_CTX_MAP[i][0];
        var styleValue = style[styleProp];
        var ctxProp = STYLE_CTX_MAP[i][1];
        if (typeof styleValue != 'undefined') {
            ctx[ctxProp] = styleValue;
        }
    }
};
var clipShapeInvTransform = matrix.create();
Base.prototype.doClip = function (ctx) {
    if (this.__clipShapes && !vmlCanvasManager) {
        for (var i = 0; i < this.__clipShapes.length; i++) {
            var clipShape = this.__clipShapes[i];
            if (clipShape.needTransform) {
                var m = clipShape.transform;
                matrix.invert(clipShapeInvTransform, m);
                ctx.transform(m[0], m[1], m[2], m[3], m[4], m[5]);
            }
            ctx.beginPath();
            clipShape.buildPath(ctx, clipShape.style);
            ctx.clip();
            // Transform back
            if (clipShape.needTransform) {
                var m = clipShapeInvTransform;
                ctx.transform(m[0], m[1], m[2], m[3], m[4], m[5]);
            }
        }
    }
};
/**
         * 根据默认样式扩展高亮样式
         * 
         * @param {module:zrender/shape/Base~IBaseShapeStyle} style 默认样式
         * @param {module:zrender/shape/Base~IBaseShapeStyle} highlightStyle 高亮样式
         * @param {string} brushTypeOnly
         */
Base.prototype.getHighlightStyle = function (style, highlightStyle, brushTypeOnly) {
    var newStyle = {};
    for (var k in style) {
        newStyle[k] = style[k];
    }
    var color = require('../tool/color');
    var highlightColor = color.getHighlightColor();
    // 根据highlightStyle扩展
    if (style.brushType != 'stroke') {
        // 带填充则用高亮色加粗边线
        newStyle.strokeColor = highlightColor;
        newStyle.lineWidth = (style.lineWidth || 1) + this.getHighlightZoom();
        newStyle.brushType = 'both';
    } else {
        if (brushTypeOnly != 'stroke') {
            // 描边型的则用原色加工高亮
            newStyle.strokeColor = highlightColor;
            newStyle.lineWidth = (style.lineWidth || 1) + this.getHighlightZoom();
        } else {
            // 线型的则用原色加工高亮
            newStyle.strokeColor = highlightStyle.strokeColor || color.mix(style.strokeColor, color.toRGB(highlightColor));
        }
    }
    // 可自定义覆盖默认值
    for (var k in highlightStyle) {
        if (typeof highlightStyle[k] != 'undefined') {
            newStyle[k] = highlightStyle[k];
        }
    }
    return newStyle;
};
// 高亮放大效果参数
// 当前统一设置为6，如有需要差异设置，通过this.type判断实例类型
Base.prototype.getHighlightZoom = function () {
    return this.type != 'text' ? 6 : 2;
};
/**
         * 移动位置
         * @param {number} dx 横坐标变化
         * @param {number} dy 纵坐标变化
         */
Base.prototype.drift = function (dx, dy) {
    this.position[0] += dx;
    this.position[1] += dy;
};
/**
         * 构建绘制的Path
         * @param {CanvasRenderingContext2D} ctx
         * @param {module:zrender/shape/Base~IBaseShapeStyle} style
         */
Base.prototype.buildPath = function (ctx, style) {
    log('buildPath not implemented in ' + this.type);
};
/**
         * 计算返回包围盒矩形
         * @param {module:zrender/shape/Base~IBaseShapeStyle} style
         * @return {module:zrender/shape/Base~IBoundingRect}
         */
Base.prototype.getRect = function (style) {
    log('getRect not implemented in ' + this.type);
};
/**
         * 判断鼠标位置是否在图形内
         * @param {number} x
         * @param {number} y
         * @return {boolean}
         */
Base.prototype.isCover = function (x, y) {
    var originPos = this.transformCoordToLocal(x, y);
    x = originPos[0];
    y = originPos[1];
    // 快速预判并保留判断矩形
    if (this.isCoverRect(x, y)) {
        // 矩形内
        return require('../tool/area').isInside(this, this.style, x, y);
    }
    return false;
};
Base.prototype.isCoverRect = function (x, y) {
    // 快速预判并保留判断矩形
    var rect = this.style.__rect;
    if (!rect) {
        rect = this.style.__rect = this.getRect(this.style);
    }
    return x >= rect.x && x <= rect.x + rect.width && y >= rect.y && y <= rect.y + rect.height;
};
/**
         * 绘制附加文本
         * @param {CanvasRenderingContext2D} ctx
         * @param {module:zrender/shape/Base~IBaseShapeStyle} style 样式
         * @param {module:zrender/shape/Base~IBaseShapeStyle} normalStyle 默认样式，用于定位文字显示
         */
Base.prototype.drawText = function (ctx, style, normalStyle) {
    if (typeof style.text == 'undefined' || style.text === false) {
        return;
    }
    // 字体颜色策略
    var textColor = style.textColor || style.color || style.strokeColor;
    ctx.fillStyle = textColor;
    // 文本与图形间空白间隙
    var dd = 10;
    var al;
    // 文本水平对齐
    var bl;
    // 文本垂直对齐
    var tx;
    // 文本横坐标
    var ty;
    // 文本纵坐标
    var textPosition = style.textPosition || this.textPosition || 'top';
    // 全局默认
    switch (textPosition) {
    case 'inside':
    case 'top':
    case 'bottom':
    case 'left':
    case 'right':
        if (this.getRect) {
            var rect = (normalStyle || style).__rect || this.getRect(normalStyle || style);
            switch (textPosition) {
            case 'inside':
                tx = rect.x + rect.width / 2;
                ty = rect.y + rect.height / 2;
                al = 'center';
                bl = 'middle';
                if (style.brushType != 'stroke' && textColor == style.color) {
                    ctx.fillStyle = '#fff';
                }
                break;
            case 'left':
                tx = rect.x - dd;
                ty = rect.y + rect.height / 2;
                al = 'end';
                bl = 'middle';
                break;
            case 'right':
                tx = rect.x + rect.width + dd;
                ty = rect.y + rect.height / 2;
                al = 'start';
                bl = 'middle';
                break;
            case 'top':
                tx = rect.x + rect.width / 2;
                ty = rect.y - dd;
                al = 'center';
                bl = 'bottom';
                break;
            case 'bottom':
                tx = rect.x + rect.width / 2;
                ty = rect.y + rect.height + dd;
                al = 'center';
                bl = 'top';
                break;
            }
        }
        break;
    case 'start':
    case 'end':
        var pointList = style.pointList || [
            [
                style.xStart || 0,
                style.yStart || 0
            ],
            [
                style.xEnd || 0,
                style.yEnd || 0
            ]
        ];
        var length = pointList.length;
        if (length < 2) {
            // 少于2个点就不画了~
            return;
        }
        var xStart;
        var xEnd;
        var yStart;
        var yEnd;
        switch (textPosition) {
        case 'start':
            xStart = pointList[1][0];
            xEnd = pointList[0][0];
            yStart = pointList[1][1];
            yEnd = pointList[0][1];
            break;
        case 'end':
            xStart = pointList[length - 2][0];
            xEnd = pointList[length - 1][0];
            yStart = pointList[length - 2][1];
            yEnd = pointList[length - 1][1];
            break;
        }
        tx = xEnd;
        ty = yEnd;
        var angle = Math.atan((yStart - yEnd) / (xEnd - xStart)) / Math.PI * 180;
        if (xEnd - xStart < 0) {
            angle += 180;
        } else if (yStart - yEnd < 0) {
            angle += 360;
        }
        dd = 5;
        if (angle >= 30 && angle <= 150) {
            al = 'center';
            bl = 'bottom';
            ty -= dd;
        } else if (angle > 150 && angle < 210) {
            al = 'right';
            bl = 'middle';
            tx -= dd;
        } else if (angle >= 210 && angle <= 330) {
            al = 'center';
            bl = 'top';
            ty += dd;
        } else {
            al = 'left';
            bl = 'middle';
            tx += dd;
        }
        break;
    case 'specific':
        tx = style.textX || 0;
        ty = style.textY || 0;
        al = 'start';
        bl = 'middle';
        break;
    }
    if (tx != null && ty != null) {
        _fillText(ctx, style.text, tx, ty, style.textFont, style.textAlign || al, style.textBaseline || bl);
    }
};
Base.prototype.modSelf = function () {
    this.__dirty = true;
    if (this.style) {
        this.style.__rect = null;
    }
    if (this.highlightStyle) {
        this.highlightStyle.__rect = null;
    }
};
/**
         * 图形是否会触发事件
         * @return {boolean}
         */
// TODO, 通过 bind 绑定的事件
Base.prototype.isSilent = function () {
    return !(this.hoverable || this.draggable || this.clickable || this.onmousemove || this.onmouseover || this.onmouseout || this.onmousedown || this.onmouseup || this.onclick || this.ondragenter || this.ondragover || this.ondragleave || this.ondrop);
};
util.merge(Base.prototype, Transformable.prototype, true);
util.merge(Base.prototype, Eventful.prototype, true);
module.exports = Base || module.exports;;